package com.leyou.order.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.leyou.client.item.ItemClient;
import com.leyou.client.user.UserClient;
import com.leyou.common.auth.pojo.UserHolder;
import com.leyou.common.auth.pojo.UserInfo;
import com.leyou.common.constants.LyConstants;
import com.leyou.common.constants.MQConstants;
import com.leyou.common.exception.pojo.ExceptionEnum;
import com.leyou.common.exception.pojo.LyException;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.IdWorker;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.SeckillGoodsDTO;
import com.leyou.item.pojo.Sku;
import com.leyou.order.dto.CartDTO;
import com.leyou.order.dto.OrderDTO;
import com.leyou.order.dto.OrderVO;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderLogisticsMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.pojo.Order;
import com.leyou.order.pojo.OrderDetail;
import com.leyou.order.pojo.OrderLogistics;
import com.leyou.order.pojo.OrderStatusEnum;
import com.leyou.order.utils.PayHelper;
import com.leyou.user.pojo.AddressDTO;

import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单业务
 */
@Service
@Transactional
@Slf4j
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private OrderLogisticsMapper orderLogisticsMapper;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private ItemClient itemClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private PayHelper payHelper;
    @Autowired
    private AmqpTemplate amqpTemplate;

    @GlobalTransactional
    public Long buildOrder(OrderDTO orderDTO) {
        //获取当前登录有用户信息
        UserInfo user = UserHolder.getUser();

        //1.保存订单信息
        Order order = new Order();
        //1.1 订单号
        order.setOrderId(idWorker.nextId());
        
        //1.2 总金额
        //1）获取所有商品参数
        // List<CartDTO> 格式： [ {"100":20,"200":15....}  ]
        List<CartDTO> carts = orderDTO.getCarts();

        //2）把List<CartDTO>转换为Map结构：   {"100":20,"200":15....}
        //Collectors.toMap():
        //  参数一： key的映射。 收集的Map集合的key元素来源于carts集合中元素（CartDTO）的哪个字段
        //  参数二： value的映射。 收集的Map集合的value元素来源于carts集合中元素（CartDTO）的哪个字段
        Map<Long, Integer> cartMap = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
        Map<Long, String> cartSpecMap = carts.stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getOwnSpec));

        //3）获取skuId集合
        List<Long> skuIdList = carts.stream().map(CartDTO::getSkuId).collect(Collectors.toList());

        //4）根据skuId集合查询Sku对象集合
        List<Sku> skuList = itemClient.findSkusByIds(skuIdList);

        //5）计算商品总价
        Long totalFee = skuList.stream().mapToLong(sku -> sku.getPrice()*cartMap.get(sku.getId())).sum();

        order.setTotalFee(totalFee);

        //1.3 实付金额
        order.setActualFee(1L);
        //1.4 优惠活动ID
        order.setPromotionIds("1");
        //1.5 支付类型
        order.setPaymentType(orderDTO.getPaymentType());
        //1.6 邮费
        order.setPostFee(0L);
        //1.7 用户ID
        order.setUserId(user.getId());
        //1.8 发票类型
        order.setInvoiceType(0);
        //1.9 订单来源
        order.setSourceType(2);
        //1.10 订单状态
        order.setStatus(OrderStatusEnum.INIT.value());

        orderMapper.insert(order);

        //2.保存订单详情信息
        //1）遍历所有Sku对象
        skuList.forEach(sku -> {
            OrderDetail orderDetail = new OrderDetail();
            //2.1 详情ID
            orderDetail.setId(idWorker.nextId());
            //2.2 订单号
            orderDetail.setOrderId(order.getOrderId());
            //2.3 商品id
            orderDetail.setSkuId(sku.getId());
            //2.4 商品数量
            orderDetail.setNum(cartMap.get(sku.getId()));
            //2.5 标题
            orderDetail.setTitle(sku.getTitle());
            //2.6 特有参数
            orderDetail.setOwnSpec(cartSpecMap.get(sku.getId()));
            //2.7 价格
            orderDetail.setPrice(sku.getPrice());
            //2.8 图片
            orderDetail.setImage(sku.getImages());

            orderDetailMapper.insert(orderDetail);
        });

        //3.保存订单物流信息
        AddressDTO addressDTO = userClient.findAddressById(user.getId(), orderDTO.getAddressId());

        //3.1 把收货人信息拷贝到OrderLogistics对象中
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO,OrderLogistics.class);
        //3.2 订单号
        orderLogistics.setOrderId(order.getOrderId());
        //3.3 物流单号
        orderLogistics.setLogisticsNumber("2232323");
        //3.4 物流公司
        orderLogistics.setLogisticsCompany("菜鸟物流");

        orderLogisticsMapper.insert(orderLogistics);

        //4.扣减购买商品的库存量
        itemClient.minusStock(cartMap);

        //模拟异常
        //int i = 10/0;

        //订单创建后发送MQ删除购物车信息
        //3）把用户ID和skuID一起发给MQ
        Map<String, Object> msgMap = new HashMap<>();
        msgMap.put("userId", user.getId().toString());
        msgMap.put("skuList", skuIdList);
        amqpTemplate.convertAndSend(
                MQConstants.Exchange.ORDER_EXCHANGE_NAME,
                MQConstants.RoutingKey.ORDER_CART_KEY,
                msgMap);

        return order.getOrderId();
    }

    public OrderVO findOrderById(Long id) {
        //1.查询订单表数据
        Order order = orderMapper.selectById(id);

        //判断订单状态是否正常
        if (order.getStatus() == 5) {
            payHelper.closeOrder(id, order.getActualFee());
            throw new LyException(500, "订单已关闭");
        }

        //2.把Order拷贝到OrderVo
        OrderVO orderVO = BeanHelper.copyProperties(order, OrderVO.class);

        //2.查询订单详情
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderId(id);
        QueryWrapper<OrderDetail> orderDetailQueryWrapper = Wrappers.query(orderDetail);
        List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailQueryWrapper);
        orderVO.setDetailList(orderDetails);

        //3.查询订单物流
        OrderLogistics orderLogistics = orderLogisticsMapper.selectById(id);
        orderVO.setLogistics(orderLogistics);

        return orderVO;
    }

    public String getPayUrl(Long id) {
        String payKey = LyConstants.PAY_URL_PRE+id;

        //根据订单ID查询订单
        Order order = orderMapper.selectById(id);

        //用户下单后，20分钟到2小时之间，支付过期的订单，依然可以付款，
        // 但有可能库存不足，导致付款失败，如果失败，将订单状态变成已失效。
        if (order.getStatus() == 7) {
            //查询订单详情
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(order.getOrderId());
            QueryWrapper<OrderDetail> orderDetailQueryWrapper = Wrappers.query(orderDetail);
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailQueryWrapper);
            Map<Long, Integer> resultMap = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));
            //查询库存
            try {
                itemClient.checkStock(resultMap);
            } catch (Exception e) {
                throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH_ERROR);
            }
        }

        //判断订单状态是否正常
        if (order.getStatus() == 5) {
             payHelper.closeOrder(id, order.getActualFee());
            throw new LyException(500, "订单已关闭");
        }

        //1.从redis取出支付链接
        String payUrl = redisTemplate.opsForValue().get(payKey);

        //2.判断redis是否存在该订单的支付链接
        if(payUrl!=null){
            //4.如果存在支付链接，则直接取出返回即可
            return payUrl;
        }


        //3.如果不存在支付链接，则微信下单API获取支付链接
        payUrl = payHelper.getPayUrl(id,order.getActualFee());

        //4. 把支付链接存入redis，设置有效期为3小时
        redisTemplate.opsForValue().set(payKey,payUrl,3, TimeUnit.HOURS);

        //5.返回生成的支付链接
        return payUrl;
    }

    public void wxNotify(Map<String, String> paramsMap) {
        //1.获取订单号和支付金额
        Long orderId =  Long.valueOf(paramsMap.get("out_trade_no"));
        Long totalFee = Long.valueOf(paramsMap.get("total_fee"));

        //2.根据订单号查询订单
        Order order = orderMapper.selectById(orderId);

        //3.判断订单是否存在，如果不存在，抛出异常
        if(order==null){
            log.error("【支付回调】订单不存在！");
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }

        //4.判断支付金额是否一致
        if(order.getActualFee()!=totalFee){
            log.error("【支付回调】支付金额不一致！");
            throw new LyException(ExceptionEnum.PAY_FEE_NOT_ERROR);
        }

        //5.判断订单状态如果已经支付过了，无需支付了
        if(order.getStatus()!=1 && order.getStatus()!=7){
            log.error("【支付回调】订单已经支付，无需重复支付！");
            throw new LyException(ExceptionEnum.ORDER_HAS_PAY_ERROR);
        }
        // 过期订单支付成功后扣减库存
        if (order.getStatus() == 7) {
            //查询订单详情
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(order.getOrderId());
            QueryWrapper<OrderDetail> orderDetailQueryWrapper = Wrappers.query(orderDetail);
            List<OrderDetail> orderDetails = orderDetailMapper.selectList(orderDetailQueryWrapper);
            Map<Long, Integer> resultMap = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getSkuId, OrderDetail::getNum));
            //查询库存
            try {
                itemClient.minusStock(resultMap);
            } catch (Exception e) {
                throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH_ERROR);
            }
        }

        //6.更新订单状态以及支付时间
        try {
            order.setStatus(2);
            order.setPayTime(new Date());
            orderMapper.updateById(order);

            log.info("【支付回调】订单状态更新成功");


        } catch (Exception e) {
            e.printStackTrace();
            log.error("【支付回调】订单状态更新失败");
        }


    }

    public Integer checkState(Long id) {
        Order order = orderMapper.selectById(id);
        //判断订单状态是否正常
        if (order.getStatus() == 5) {
            payHelper.closeOrder(id, order.getActualFee());
        }
        if (order == null) {
            throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);
        }
        return order.getStatus();
    }

    public List<Order> findAll() {
        return orderMapper.selectList(null);
    }

    public Integer addFavorite(Long skuId) {
        UserInfo user = UserHolder.getUser();
        ResponseEntity<Integer> integerResponseEntity = itemClient.favoriteGoods(skuId, user.getId());
        return integerResponseEntity.getBody();
    }

    public void createSkuSeckillOrder(Map<String, Object> map) {
        Object o = map.get("orderDTO");
        OrderDTO orderDTO = JsonUtils.toBean(o.toString(), OrderDTO.class);
        Object orderIdObj = map.get("orderId");
        Object userIdObj = map.get("userId");

        Long orderId = Long.valueOf(orderIdObj.toString());
        Long userId = Long.valueOf(userIdObj.toString());

        //1.保存订单信息
        Order order = new Order();
        //1.1 订单号
        order.setOrderId(orderId);

        //1.2 总金额
        //1）获取所有商品参数
        // List<CartDTO> 格式： [ {"100":20,"200":15....}  ]
        SeckillGoodsDTO seck = orderDTO.getSeck();

        //2）把List<CartDTO>转换为Map结构：   {"100":20,"200":15....}
        //Collectors.toMap():
        //  参数一： key的映射。 收集的Map集合的key元素来源于carts集合中元素（CartDTO）的哪个字段
        //  参数二： value的映射。 收集的Map集合的value元素来源于carts集合中元素（CartDTO）的哪个字段
        Map<Long, Integer> cartMap = new HashMap<>();
        cartMap.put(seck.getSkuId(), 1);

        //3）获取skuId集合
        List<Long> skuIdList = new ArrayList<>();
        skuIdList.add(seck.getSkuId());

        //4）根据skuId集合查询Sku对象集合
        List<Sku> skuList = itemClient.findSkusByIds(skuIdList);

        //5）计算商品总价
        Long totalFee = skuList.stream().mapToLong(sku -> sku.getPrice() * cartMap.get(sku.getId())).sum();

        order.setTotalFee(totalFee);

        //1.3 实付金额
        order.setActualFee(1L);
        //1.4 优惠活动ID
        order.setPromotionIds("1");
        //1.5 支付类型
        order.setPaymentType(orderDTO.getPaymentType());
        //1.6 邮费
        order.setPostFee(0L);
        //1.7 用户ID
        order.setUserId(userId);
        //1.8 发票类型
        order.setInvoiceType(0);
        //1.9 订单来源
        order.setSourceType(2);
        //1.10 订单状态
        order.setStatus(OrderStatusEnum.INIT.value());

        orderMapper.insert(order);

        //2.保存订单详情信息
        //1）遍历所有Sku对象
        skuList.forEach(sku -> {
            OrderDetail orderDetail = new OrderDetail();
            //2.1 详情ID
            orderDetail.setId(idWorker.nextId());
            //2.2 订单号
            orderDetail.setOrderId(order.getOrderId());
            //2.3 商品id
            orderDetail.setSkuId(sku.getId());
            //2.4 商品数量
            orderDetail.setNum(cartMap.get(sku.getId()));
            //2.5 标题
            orderDetail.setTitle(sku.getTitle());
            //2.6 特有参数
            orderDetail.setOwnSpec(sku.getOwnSpec());
            //2.7 价格
            orderDetail.setPrice(sku.getPrice());
            //2.8 图片
            orderDetail.setImage(sku.getImages());

            orderDetailMapper.insert(orderDetail);
        });

        //3.保存订单物流信息
        AddressDTO addressDTO = userClient.findAddressById(userId, orderDTO.getAddressId());

        //3.1 把收货人信息拷贝到OrderLogistics对象中
        OrderLogistics orderLogistics = BeanHelper.copyProperties(addressDTO, OrderLogistics.class);
        //3.2 订单号
        orderLogistics.setOrderId(order.getOrderId());
        //3.3 物流单号
        orderLogistics.setLogisticsNumber("2232323");
        //3.4 物流公司
        orderLogistics.setLogisticsCompany("菜鸟物流");

        orderLogisticsMapper.insert(orderLogistics);

        //4.扣减购买商品的库存量
        Long skuId = seck.getSkuId();
        itemClient.minusSeckillStock(skuId);


    }
}
